package org.hbhk.hxl.gateway.services;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.hbhk.hxl.gateway.boot.ApplicationContextUtil;
import org.hbhk.hxl.gateway.model.CacheUrl;
import org.hbhk.hxl.gateway.model.Circuitbreaker;
import org.hbhk.hxl.gateway.model.HttpNode;
import org.hbhk.hxl.gateway.model.HttpService;
import org.hbhk.hxl.gateway.model.Isolation;
import org.hbhk.hxl.gateway.model.RateLimit;
import org.hbhk.hxl.gateway.model.ServiceNode;
import org.hbhk.hxl.gateway.route.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;

@Component("hmsDefaultService")
public class HmsDefaultService implements IGatewayService, InitializingBean {

	private Logger log = LoggerFactory.getLogger(getClass());

	private Map<String, HttpService> serviceLkeCache = Maps.newConcurrentMap();

	private Map<String, HttpService> serviceCache = Maps.newConcurrentMap();

	public static Map<String, List<String>> nodeCache = Maps.newConcurrentMap();

	public static Map<String, List<String>> grayNodeCache = Maps.newConcurrentMap();

	private Map<String, List<String>> servicesNodeCache = Maps.newConcurrentMap();

	private Map<String, String> urlToAppCache = Maps.newConcurrentMap();

	private Map<String, Circuitbreaker> breakerCache = Maps.newConcurrentMap();

	private Map<String, Isolation> isolationCache = Maps.newConcurrentMap();

	private Map<String, RateLimiter> rateLimitCache = Maps.newConcurrentMap();

	private Map<String, Integer> cacheUrlMap = Maps.newConcurrentMap();

	@Autowired
	private JdbcTemplate jdbcTemplate;

	private ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

	@Override
	public HttpService getHttpService(String url) {
		// 精确匹配
		HttpService httpService = serviceCache.get(url);
		if (httpService != null) {
			return httpService;
		}

		if (serviceLkeCache == null || serviceLkeCache.size() == 0) {
			return null;
		}
		Set<String> urlLike = serviceLkeCache.keySet();

		for (String str : urlLike) {
			String prefix = str.replace("**", "").trim();
			if (url.startsWith(prefix)) {
				return serviceLkeCache.get(str);
			}
		}
		return null;
	}

	@Override
	public List<String> getDisableNode(String service) {
		List<String> nodeSet = servicesNodeCache.get(service);
		return nodeSet;
	}

	@Override
	public Circuitbreaker getBreaker(String url) {
		return breakerCache.get(url);
	}

	@Override
	public Isolation getIsolation(String url) {
		return isolationCache.get(url);
	}

	@Override
	public RateLimiter getRateLimit(String url) {
		return rateLimitCache.get(url);
	}

	private int internal = 300;;

	@Override
	public void afterPropertiesSet() throws Exception {
		ApplicationContextUtil.getApplicationContext().getBean(NettyRoutingFilter.class);
		ApplicationContextUtil.unregisterBean("routingFilter");
		// loadDynamicRoute(null, null);
		loadServices(null, null);
		loadNodes();
		loadCacheUrl();

		loadRatelimit(null, null);
		loadBreaker(null, null);
		loadIsolation(null, null);

		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {

				try {
					loadRatelimit(null, null);
					loadBreaker(null, null);
					loadIsolation(null, null);
				} catch (Exception e) {
					log.error("加载服务管理信息错误", e);
				}
			}

		}, internal, internal, TimeUnit.SECONDS);

		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {

				try {
					loadServices(null, null);
					loadNodes();
					loadCacheUrl();
				} catch (Exception e) {
					log.error("加载服务管理信息错误", e);
				}

			}

		}, internal, internal, TimeUnit.SECONDS);
	}

	private void loadServices(Long dataId, String mode) {

		String sql = "select url, enable,timeout,info,app_name from hms_gateway_service";
		if (dataId != null) {
			sql = sql + " where id=" + dataId;
		}
		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql);
		if (allMap == null || allMap.isEmpty()) {
			serviceLkeCache.clear();
			serviceCache.clear();
			urlToAppCache.clear();

			return;
		}
		List<HttpService> httpServices = convert(allMap, HttpService.class);
		for (HttpService httpService : httpServices) {
			String url = httpService.getUrl();
			urlToAppCache.put(url, httpService.getAppName());
			if (url.endsWith("**")) {
				httpService.setLike(true);
				String key = JSON.toJSONString(httpService);
				if (serviceLkeCache.containsKey(url)) {
					if (!key.equals(JSON.toJSONString(serviceLkeCache.get(url)))) {
						serviceLkeCache.put(httpService.getUrl(), httpService);
						log.info("变更接口" + httpService.getUrl() + "配置信息:" + JsonUtil.objectToJson(httpService));
					}
				} else {
					serviceLkeCache.put(httpService.getUrl(), httpService);
					log.info("新增接口" + httpService.getUrl() + "配置信息:" + JsonUtil.objectToJson(httpService));
				}
			} else {
				String key = JSON.toJSONString(httpService);
				if (serviceCache.containsKey(url)) {
					if (!key.equals(JSON.toJSONString(serviceCache.get(url)))) {
						serviceCache.put(httpService.getUrl(), httpService);
						log.info("变更接口" + httpService.getUrl() + "配置信息:" + JsonUtil.objectToJson(httpService));
					}
				} else {
					serviceCache.put(httpService.getUrl(), httpService);
					log.info("新增接口" + httpService.getUrl() + "配置信息:" + JsonUtil.objectToJson(httpService));
				}
			}

		}
	}

	@SuppressWarnings("unchecked")
	private <T> List<T> convert(List<Map<String, Object>> list, Class<T> cls) {
		List<T> httpServices = Lists.newArrayList();
		try {
			Field[] fields = cls.getDeclaredFields();
			Map<String, Field> fs = Maps.newConcurrentMap();
			for (Field field : fields) {
				fs.put(field.getName(), field);
			}
			for (Map<String, Object> map : list) {
				Set<String> keys = map.keySet();
				Object obj = cls.newInstance();
				for (String key : keys) {
					String f = key;
					if (key.indexOf("_") > -1) {
						String[] ar = key.split("_");
						StringBuffer sb = new StringBuffer();
						for (int i = 0; i < ar.length; i++) {
							if (i == 0) {
								sb.append(ar[i]);
							} else {
								sb.append(ar[i].substring(0, 1).toUpperCase() + ar[i].substring(1, ar[i].length()));
							}
						}
						f = sb.toString();
					}
					if (!fs.containsKey(f)) {
						continue;
					}
					fs.get(f).setAccessible(true);
					fs.get(f).set(obj, map.get(key));
				}
				httpServices.add((T) obj);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return httpServices;

	}

	private void loadNodes() {
		loadServicesNodes();
		String sql = "select  app,node, enable,gray from hms_gateway_node  where  enable = '1'";
		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql);
		Map<String, List<String>> nodeMap = Maps.newHashMap();
		Map<String, List<String>> grayNodeMap = Maps.newHashMap();
		if (allMap == null || allMap.isEmpty()) {
			nodeCache = nodeMap;
			grayNodeCache = grayNodeMap;
			return;
		}
		List<HttpNode> httpServices = convert(allMap, HttpNode.class);

		for (HttpNode node : httpServices) {
			String app = node.getApp();
			String en = node.getEnable();
			String gray = node.getGray();
			if ("1".equals(en)) {
				if ("2".equals(gray)) {
					if (grayNodeMap.containsKey(app)) {
						grayNodeMap.get(app).add(node.getNode());
					} else {
						List<String> nodeSet = Lists.newArrayList();
						nodeSet.add(node.getNode());
						grayNodeMap.put(app, nodeSet);
					}
				} else {
					if (nodeMap.containsKey(app)) {
						nodeMap.get(app).add(node.getNode());
					} else {
						List<String> nodeSet = Lists.newArrayList();
						nodeSet.add(node.getNode());
						nodeMap.put(app, nodeSet);
					}
				}

			}

		}
		nodeCache = nodeMap;
		grayNodeCache = grayNodeMap;

	}

	private void loadServicesNodes() {
		String sql = "select  url,node, enable from hms_gateway_service_node  where enable= '0'";
		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql);
		Map<String, List<String>> nodeMap = Maps.newHashMap();

		if (allMap == null || allMap.isEmpty()) {
			servicesNodeCache = nodeMap;

			return;
		}
		List<ServiceNode> httpServices = convert(allMap, ServiceNode.class);

		for (ServiceNode node : httpServices) {
			String app = node.getUrl();
			if (nodeMap.containsKey(app)) {
				nodeMap.get(app).add(node.getNode());
			} else {
				List<String> nodeSet = Lists.newArrayList();
				nodeSet.add(node.getNode());
				nodeMap.put(app, nodeSet);
			}

		}
		servicesNodeCache = nodeMap;
	}

	private void loadRatelimit(Long dataId, String mode) {
		String sql = "select r.time,r.counter,s.interface_code "
				+ "from hms_gateway_ratelimit r,hms_gateway_service_config s "
				+ "where s.type = 'ratelimit'  and r.group_code = s.group_code and r.enable='1'";
		if (dataId != null) {
			sql = sql + " and  r.id=" + dataId;
		}

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
		List<RateLimit> convertList = convert(list, RateLimit.class);
		Map<String, RateLimiter> rateLimits = Maps.newConcurrentMap();

		for (RateLimit node : convertList) {
			String code = node.getInterfaceCode();
			Integer timer = node.getTime();
			Integer count = node.getCounter();
			RateLimiter rateLimiter = null;
			if (timer == null || timer == 0) {
				rateLimiter = RateLimiter.create(count);
			} else {
				rateLimiter = RateLimiter.create(count, timer, TimeUnit.SECONDS);
			}
			rateLimits.put(code, rateLimiter);

		}
		rateLimitCache = rateLimits;

	}

	private void loadIsolation(Long dataId, String mode) {
		String sql = "select r.time,r.counter,s.interface_code "
				+ "from hms_gateway_isolation r,hms_gateway_service_config s "
				+ "where s.type = 'isolation'  and r.group_code = s.group_code and r.enable='1'";

		if (dataId != null) {
			sql = sql + " and  r.id=" + dataId;
		}

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
		List<Isolation> convertList = convert(list, Isolation.class);
		for (Isolation node : convertList) {
			String code = node.getInterfaceCode();
			String key = JsonUtil.objectToJson(node);
			if (isolationCache.containsKey(code)) {
				if (!key.equals(JsonUtil.objectToJson(isolationCache.get(code)))) {
					isolationCache.put(code, node);
					log.info("接口" + node.getInterfaceCode() + "熔断方案:" + JsonUtil.objectToJson(node));
				}
			} else {
				isolationCache.put(code, node);
				log.info("接口" + node.getInterfaceCode() + "熔断方案:" + JsonUtil.objectToJson(node));
			}
		}
	}

	private void loadBreaker(Long dataId, String mode) {
		String sql = "select r.timeout,r.success_threshold,r.failure_threshold,s.interface_code "
				+ "from hms_gateway_breaker r,hms_gateway_service_config s "
				+ "where s.type = 'breaker'  and r.group_code = s.group_code and r.enable='1'";

		if (dataId != null) {
			sql = sql + " and  r.id=" + dataId;
		}
		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
		List<Circuitbreaker> convertList = convert(list, Circuitbreaker.class);
		for (Circuitbreaker node : convertList) {
			String code = node.getInterfaceCode();
			String key = JsonUtil.objectToJson(node);
			if (breakerCache.containsKey(code)) {
				if (!key.equals(JsonUtil.objectToJson(breakerCache.get(code)))) {
					breakerCache.put(code, node);
					log.info("接口" + node.getInterfaceCode() + "隔离方案:" + JsonUtil.objectToJson(node));
				}
			} else {
				breakerCache.put(code, node);
				log.info("接口" + node.getInterfaceCode() + "隔离方案:" + JsonUtil.objectToJson(node));
			}
		}
	}

	private void loadCacheUrl() {
		String sql = "select r.time,s.interface_code " + "from hms_gateway_cache r,hms_gateway_service_config s "
				+ "where s.type = 'cacheUrl'  and r.group_code = s.group_code and r.enable='1'";

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
		List<CacheUrl> convertList = convert(list, CacheUrl.class);

		Map<String, Integer> cacheUrlDb = Maps.newConcurrentMap();

		for (CacheUrl node : convertList) {
			String code = node.getInterfaceCode();
			Integer time = node.getTime();
			cacheUrlDb.put(code, time);
		}

		cacheUrlMap = cacheUrlDb;
	}

	public void loadDynamicRoute(Long dataId, String mode) {
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@Override
	public List<String> getNode(String service) {
		return nodeCache.get(service);
	}

	@Override
	public List<String> getGrayNode(String service) {
		return grayNodeCache.get(service);
	}

	@Override
	public String getApp(String service) {
		return urlToAppCache.get(service);
	}

	@Override
	public Integer getCacheUrl(String url) {
		return cacheUrlMap.get(url);
	}

	@Override
	public void reset(String msg) {
		try {
			switch (msg) {
			case "httpService":
				loadServices(null, null);
				log.info("结束刷新gateway数据:" + msg);
				break;
			case "breaker":
				loadBreaker(null, null);
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "isolation":
				loadIsolation(null, null);
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "rateLimit":
				loadRatelimit(null, null);
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "serviceConfig":
				loadIsolation(null, null);
				loadRatelimit(null, null);
				loadBreaker(null, null);
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "node":
				loadNodes();
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "serviceNode":
				loadServicesNodes();
				log.info("结束刷新gateway数据:" + msg);

				break;
			case "cacheUrl":
				loadCacheUrl();
				log.info("结束刷新gateway数据:" + msg);

				break;

			default:
				break;
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}

	}

}
